package org.kiwi.job.adapter.quartz;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.kiwi.job.component.LockTemplate;
import org.kiwi.job.core.ScheduleJob;
import org.kiwi.job.orm.model.JobLogModel;
import org.kiwi.job.orm.model.JobModel;
import org.kiwi.job.service.ScheduleJobService;
import org.kiwi.job.vo.BaseResponse;
import org.kiwi.job.vo.JobLogVO;
import org.kiwi.util.IPUtil;
import org.kiwi.util.mapping.BeanMapper;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.concurrent.TimeUnit;

import static org.kiwi.job.constant.CommonConstant.OK;
import static org.kiwi.job.constant.ScheduleConstant.*;

/**
 * @email jack.liu.19910921@gmail.com
 * Created by jack on 17/2/19.
 */
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class QuartzJob implements Job {

    private static final Logger LOGGER = LoggerFactory.getLogger(QuartzJob.class);

    private static final String SUCCESS = "Success";

    @Autowired
    private BeanMapper beanMapper;

    @Autowired
    private LockTemplate lockTemplate;

    @Autowired
    private ScheduleJobService scheduleJobService;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {

        ScheduleJob scheduleJob = getScheduleJob(context);

        if (StringUtils.isBlank(scheduleJob.getJobCode())) {
            LOGGER.info("Job任务编码为空，配置错误!");
            return;
        }

        if (!isSystemJob(scheduleJob.getJobCode())) {
            //非系统默认任务(刷新任务列表)需要确认是否被禁用
            JobModel jobModel = beanMapper.map(scheduleJobService.loadJob(scheduleJob.getJobCode()), JobModel.class);
            if (jobModel == null || jobModel.getJobStatus() == JOB_STATUS_DISABLE) {
                LOGGER.info("任务[{}]当前已被禁用或找不到该任务,放弃执行...", jobModel.getJobCode());
                return;
            }
        }

        doExecute(context);
    }


    private void doExecute(JobExecutionContext context) {
        final long starTime = System.currentTimeMillis();

        ScheduleJob scheduleJob = getScheduleJob(context);

        boolean isLock = false;

        if (scheduleJob.getIsConcurrent() == JOB_CONCURRENT_TRUE) {
            //多机器执行
            if (!isSystemJob(scheduleJob.getJobCode())) {
                LOGGER.info("任务{}配置为并发执行", scheduleJob.getJobName());
            }
        } else {
            // 单机器执行
            if (lockTemplate.tryLock(scheduleJob.getJobCode(), 5, TimeUnit.MINUTES)) {
                //加锁成功
                isLock = true;
            } else {
                LOGGER.info("任务{}为单点处理任务，执行时加锁失败，放弃执行", scheduleJob.getJobName());
                return;
            }
        }

        String result = SUCCESS;
        try {
            Object value;
            if (StringUtils.isBlank(scheduleJob.getHttpPath())) {
                value = scheduleJob.getMethodInvoker().invokeMethod();
                if (isSystemJob(scheduleJob.getJobCode())) {
                    return;
                }
            } else {
                value = scheduleJob.getMethodInvoker().invokeMethod(scheduleJob.getHttpPath());
            }
            if (value != null && value instanceof BaseResponse) {
                BaseResponse resp = (BaseResponse) value;
                if (OK != resp.getStatus()) {
                    result = resp.getError() + "-" + resp.getMsg();
                }
            } else {
                LOGGER.info("触发业务[{}],响应结果非默认对象", JSONObject.toJSONString(value));
                result = "400-响应结果非默认对象";
            }
        } catch (Exception e) {
            LOGGER.error("触发任务[" + scheduleJob.getJobName() + "]异常", e);
            result = "500-触发业务调用异常" + e.getMessage();
        } finally {
            if (isLock) {
                lockTemplate.release(scheduleJob.getJobCode());
            }
            saveJobLog(context, starTime, scheduleJob, result);
        }
    }

    private boolean isSystemJob(String jobCode) {
        return REFRESH_JOB_CODE.equalsIgnoreCase(jobCode);
    }

    private ScheduleJob getScheduleJob(JobExecutionContext context) {
        return (ScheduleJob) context.getJobDetail().getJobDataMap().get(SCHEDULE_JOB);
    }

    private void saveJobLog(JobExecutionContext context, long starTime, ScheduleJob scheduleJob, String result) {
        JobLogModel logModel = new JobLogModel();
        logModel.setCostTime(System.currentTimeMillis() - starTime);
        logModel.setPreviousTime(context.getFireTime());
        logModel.setNextTime(context.getNextFireTime());
        logModel.setIpAddress(IPUtil.getLocalIP());
        logModel.setJobCode(scheduleJob.getJobCode());
        logModel.setResultDesc(StringUtils.left(result, 500));
        logModel.setJobStatus(SUCCESS.equalsIgnoreCase(result) ? JOB_EXECUTE_SUCCESS : JOB_EXECUTE_ERROR);
        scheduleJobService.saveJobLog(beanMapper.map(logModel, JobLogVO.class));
    }

}
